/*
 * Created on Jul 11, 2004
 */
package dk.aiszone.lang.autosource.generatorobjects;

import java.lang.reflect.Modifier;

import dk.aiszone.lang.autosource.generatorobjects.interfaces.GeneratorObject;

/**
 * The <code>Declaration</code> object contains the scope and key word attributes of <code>Methods</code>, <code>Variables</code> and<br>
 * <code>AbstractBodies</code>.<br>
 * The <code>Declaration</code> is used to represent the following information:
 * <ul>
 * <li>The <code>scope</code>
 * <li>Whether the <code>Object</code> is declared <b>abstract</b>
 * <li>Whether the <code>Object</code> is declarad <b>final/<b>
 * <li>Whether the <code>Object</code> is declarad <b>static/<b>
 * <li>Whether the <code>Object</code> is declarad <b>transient/<b>
 * </ul>
 * 
 * @author <a href="mailto:ais@mail.dk">Ais</a>
 * @since 1.0
 * @version 1.0
 * 
 * @see Scope
 */
public class Declaration extends GeneratorObject implements Cloneable, Comparable<Declaration> {

    private boolean isAbstract;
    private boolean isFinal;
    private boolean isStatic;
    private boolean isTransient;

    private Scope scope;

    /**
     * Default constructor for the <code>Declaration</code> class.<br>
     * Using this constructor required the <code>scope</code> to be set later on.<br>
     * 
     * @see #setScope(Scope)
     */
    public Declaration() {
        this(null);
    }

    /**
     * Constructor for the <code>Declaration</code> object.
     * 
     * @param modifiers
     *            <code>int</code> containing a structure equals to the one expected by <code>java.lang.reflect.Modifier</code> class.<br>
     *            The contents of the <code>Declaration</code> object is set according to the value of the <code>int</code>.
     */
    public Declaration(int modifiers) {
        if (Modifier.isPublic(modifiers)) {
            scope = Scope.PUBLIC;
        } else if (Modifier.isPrivate(modifiers)) {
            scope = Scope.PRIVATE;
        } else if (Modifier.isProtected(modifiers)) {
            scope = Scope.PROTECTED;
        } else {
            scope = Scope.DEFAULT;
        }

        isAbstract = Modifier.isAbstract(modifiers);
        isFinal = Modifier.isFinal(modifiers);
        isStatic = Modifier.isStatic(modifiers);
        isTransient = Modifier.isTransient(modifiers);
    }

    /**
     * Constructor for the <code>Declaration</code> class.
     * 
     * @param scope
     *            The <code>Scope</code> of the <code>Declaration</code>.
     */
    public Declaration(Scope scope) {
        this(scope, false, false, false);
    }

    /**
     * Constructor for the <code>Declaration</code> class.
     * 
     * @param scope
     *            The <code>Scope</code> of the <code>Declaration</code>.
     * @param isFinal
     *            <code>boolean</code> indicating whether the declaration is <b>final</b>
     * @param isStatic
     *            <code>boolean</code> indicating whether the declaration is <b>static</b>
     * @param isAbstract
     *            <code>boolean</code> indicating whether the declaration is <b>abstract</b>
     */
    public Declaration(Scope scope, boolean isFinal, boolean isStatic, boolean isAbstract) {
        this.scope = scope;
        this.isFinal = isFinal;
        this.isStatic = isStatic;
        this.isAbstract = isAbstract;
    }

    @Override
    public Object clone() {
        Declaration clone = new Declaration();

        clone.isAbstract = isAbstract;
        clone.isFinal = isFinal;
        clone.isStatic = isStatic;
        clone.isTransient = isTransient;

        clone.scope = scope;

        return clone;
    }

    public int compareTo(Declaration other) {
        if (scope == null) {
            return -1;
        }

        if (isStatic == other.isStatic) {
            if (isFinal == other.isFinal) {
                return scope.compareTo(other.scope);
            } else if (isFinal) {
                return -1;
            }
            return -1;
        } else if (isStatic) {
            return -1;
        }
        return 1;
    }

    /**
     * Retrieve the <code>Scope</code> of the <code>Declaration</code>
     * 
     * @return The <code>Scope</code> of the <code>Declaration</code> or <code>null</code> if unset.
     */
    public Scope getScope() {
        return scope;
    }

    /**
     * Retrieve whether the <code>Declaration</code> is <b>abstract</b>.
     * 
     * @return <code>true</code> if the <code>Declaration</code> is <b>abstract</b> - otherwise <code>false</code>.
     */
    public boolean isAbstract() {
        return isAbstract;
    }

    /**
     * Retrieve whether the <code>Declaration</code> is <b>final</b>.
     * 
     * @return <code>true</code> if the <code>Declaration</code> is <b>final</b> - otherwise <code>false</code>.
     */
    public boolean isFinal() {
        return isFinal;
    }

    /**
     * Retrieve whether the <code>Declaration</code> is <b>static</b>.
     * 
     * @return <code>true</code> if the <code>Declaration</code> is <b>static</b> - otherwise <code>false</code>.
     */
    public boolean isStatic() {
        return isStatic;
    }

    /**
     * Retrieve whether the <code>Declaration</code> is <b>transient</b>.
     * 
     * @return <code>true</code> if the <code>Declaration</code> is <b>transient</b> - otherwise <code>false</code>.
     */
    public boolean isTransient() {
        return isTransient;
    }

    /**
     * Set or update the <code>abstract</code> attribute of the <code>Declaration</code>.
     * 
     * @param isAbstract
     *            <code>true</code> if the <code>Declaration</code> should be <b>abstract</b> - otherwise <code>false</code>.
     * @return <code>this</code> <code>Declaration</code> object.
     */
    public Declaration setAbstract(boolean isAbstract) {
        this.isAbstract = isAbstract;
        return this;
    }

    /**
     * Set or update the <code>final</code> attribute of the <code>Declaration</code>.
     * 
     * @param isFinal
     *            <code>true</code> if the <code>Declaration</code> should be <b>final</b> - otherwise <code>false</code>.
     * @return <code>this</code> <code>Declaration</code> object.
     */
    public Declaration setFinal(boolean isFinal) {
        this.isFinal = isFinal;
        return this;
    }

    /**
     * Set or update the <code>scope</code> of the <code>Declaration</code>.
     * 
     * @param scope
     *            The new <code>Scope</code> of the <code>Declaration</code>
     * @return <code>this</code> <code>Declaration</code> object.
     */
    public Declaration setScope(Scope scope) {
        this.scope = scope;
        return this;
    }

    /**
     * Set or update the <code>static</code> attribute of the <code>Declaration</code>.
     * 
     * @param isStatic
     *            <code>true</code> if the <code>Declaration</code> should be <b>static</b> - otherwise <code>false</code>.
     * @return <code>this</code> <code>Declaration</code> object.
     */
    public Declaration setStatic(boolean isStatic) {
        this.isStatic = isStatic;
        return this;
    }

    /**
     * Set or update the <code>transient</code> attribute of the <code>Declaration</code>.
     * 
     * @param isTransient
     *            <code>true</code> if the <code>Declaration</code> should be <b>transient</b> - otherwise <code>false</code>.
     * @return <code>this</code> <code>Declaration</code> object.
     */
    public Declaration setTransient(boolean isTransient) {
        this.isTransient = isTransient;
        return this;
    }
}